package de.codi.similarity.sparematrix;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.reasoner.OWLReasoner;

import de.codi.CODISettings;
import de.codi.exception.CodiException;
import de.codi.read.Storage;
import de.codi.similarity.filler.IndividualFiller;
import de.codi.similarity.filler.StructuralFiller;
import de.codi.similarity.sparematrix.merger.SpareMatrixMerger;
import de.codi.string.StringPreparation;
import de.codi.string.StringSimilarity;
import de.unima.alcomox.mapping.Mapping;


public class Generator
{
    public List<Cell<? extends OWLEntity>> list;

    private Set<OWLEntity>                 source, sourceUsed;
    private Set<OWLEntity>                 target, targetUsed;
    private List<StringSimilarity>         sim;
    private SpareMatrixMerger              merger;
    private IndividualFiller               ifiller;
    private OWLReasoner                    r1, r2;


    public Generator(Set<OWLEntity> source, Set<OWLEntity> target, List<StringSimilarity> sim, SpareMatrixMerger merger)
    {
        this.source = source;
        this.target = target;
        this.sourceUsed = new HashSet<OWLEntity>();
        this.targetUsed = new HashSet<OWLEntity>();
        this.sim = sim;
        this.merger = merger;
        this.list = new ArrayList<Cell<? extends OWLEntity>>();
        if(CODISettings.individualFiller) {
            this.ifiller = new IndividualFiller(Storage.source.ont.getIndividualsInSignature(), Storage.target.ont.getIndividualsInSignature());
        }
        this.r1 = Storage.source.reasoner;
        this.r2 = Storage.target.reasoner;

        generate();
    }


    private void generate()
    {
        for(OWLEntity e1 : source) {
            // System.out.println("E " + ++i);
            for(OWLEntity e2 : target) {
                Set<String> sourceSet = Storage.source.info.get(e1);
                Set<String> targetSet = Storage.target.info.get(e2);

                // string similarity
                double stringConf = evaluateEntitiesString(sourceSet, targetSet);

                // check equals
                if(stringConf == 1 && CODISettings.stringNotEqualsPenalty == true) {
                    boolean equals = false;
                    x: for(String s : sourceSet) {
                        for(String t : targetSet) {
                            if(s.equalsIgnoreCase(t)) {
                                equals = true;
                                break x;
                            }
                        }
                    }
                    if(!equals) {
                        stringConf -= CODISettings.PENALTY;
                    }
                }

                // instance similarity
                double instanceConf = 0;
                if(CODISettings.individualFiller) {
                    if(e1.isOWLClass() && e2.isOWLClass()) {
                        Set<OWLNamedIndividual> a = r1.getInstances((OWLClass) e1, true).getFlattened();
                        Set<OWLNamedIndividual> b = r2.getInstances((OWLClass) e2, true).getFlattened();

                        instanceConf = ifiller.compare(a, b);
                        if(instanceConf > 0) {
                            list.add(new Cell<OWLEntity>(e1, e2, instanceConf));
                            sourceUsed.add(e1);
                            targetUsed.add(e2);
                        }
                    }
                }
                double conf = Math.max(stringConf, instanceConf);
                if(conf > CODISettings.SIMPLE_THRESHOLD_DEFAULT) {
                    list.add(new Cell<OWLEntity>(e1, e2, conf));
                    sourceUsed.add(e1);
                    targetUsed.add(e2);
                }
            }
        }
        if(CODISettings.structuralFiller) {
            structural();
        }
    }


    public void structural()
    {
        Map<Integer, Cell<? extends OWLEntity>> tmpList = new HashMap<Integer, Cell<? extends OWLEntity>>();
        Map<OWLEntity, HashSet<Integer>> pos = new HashMap<OWLEntity, HashSet<Integer>>();

        Set<OWLEntity> s = new HashSet<OWLEntity>();
        Set<OWLEntity> t = new HashSet<OWLEntity>();

        for(OWLEntity oe : source) {
            if(!sourceUsed.contains(oe)) {
                s.add(oe);
            }
        }
        for(OWLEntity oe : target) {
            if(!targetUsed.contains(oe)) {
                t.add(oe);
            }
        }

        int count = 0;
        for(OWLEntity e1 : s) {
            for(OWLEntity e2 : t) {
                double confidenceValue = StructuralFiller.fill(e1, e2);
                tmpList.put(count, new Cell<OWLEntity>(e1, e2, confidenceValue));

                if(pos.containsKey(e1)) {
                    pos.get(e1).add(count);
                } else {
                    pos.put(e1, new HashSet<Integer>(count));
                }
                if(pos.containsKey(e2)) {
                    pos.get(e2).add(count);
                } else {
                    pos.put(e2, new HashSet<Integer>(count));
                }

                count++;
            }
        }

        List<Cell<? extends OWLEntity>> result = new ArrayList<Cell<? extends OWLEntity>>();
        for(OWLEntity e : pos.keySet()) {
            List<Cell<? extends OWLEntity>> tmpList2 = new ArrayList<Cell<? extends OWLEntity>>();

            Set<Integer> values = pos.get(e);

            for(Integer i : values) {
                tmpList2.add(tmpList.get(i));
            }
            if(tmpList2.size() == 0)
                continue;

            Collections.sort(tmpList2);

            int top = CODISettings.TOPVALUES;
            double bestScore = tmpList2.get(0).getConfidenceValue();
            ArrayList<Cell<? extends OWLEntity>> topCells = new ArrayList<Cell<? extends OWLEntity>>();
            // if(tmpList2.size() >= top) {
            for(int i = 0; i < Math.min(top, tmpList2.size()); i++) {
                double currentScore = tmpList2.get(i).getConfidenceValue();
                if(currentScore < bestScore) {
                    bestScore = currentScore;
                    result.addAll(topCells);
                } else {
                    topCells.add(tmpList2.get(i));
                }
            }
            // }
            // else {
            // result.addAll(tmpList2);
            // }
        }
        list.addAll(result);
    }


    private double evaluateEntitiesString(Set<String> sourceSet, Set<String> targetSet)
    {
        Map<Integer, Double> values = new HashMap<Integer, Double>();
        for(StringSimilarity ss : sim) {

            // penalty preparation 1
            List<String> sourceList = getStringList(sourceSet);
            List<String> targetList = getStringList(targetSet);
            int nSource = sourceList.size();
            int nTarget = targetList.size();

            double conf = 0.0;

            for(String source : sourceSet) {
                for(String target : targetSet) {
                    List<String> value = StringPreparation.shortCut(source, target);
                    source = value.get(0);
                    target = value.get(1);
                    double confTemp = ss.getSim(source, target);
                    if(CODISettings.preliminaryString) {
                        confTemp = Math.min(1.0, confTemp + StringPreparation.preliminary(source, target));
                    }
                    // penalty preparation 2
                    if(CODISettings.penaltyString && confTemp > 0) {
                        checkPenalty(sourceList, source);
                        checkPenalty(targetList, target);
                    }
                    if(confTemp > conf) {
                        conf = confTemp;
                    }
                }
            }
            // penalty
            if(CODISettings.penaltyString) {
                double penaltyTemp = Math.pow(((double) sourceList.size() / nSource), 2);
                penaltyTemp += Math.pow(((double) targetList.size() / nTarget), 2);

                double penalty = Math.min(CODISettings.PENALTYSPACE, penaltyTemp);
                if(conf - penalty > 0) {
                    values.put(ss.getID(), conf - penalty);
                }
            } // no penalty
            else {
                values.put(ss.getID(), conf);
            }

        }
        return merger.getConfValue(values);
    }


    private List<String> getStringList(Set<String> stringSet)
    {
        List<String> stringList = new ArrayList<String>();
        for(String s : stringSet) {
            String[] words = s.split(" ");
            if(s.length() > CODISettings.MAXCHARACTER || words.length > CODISettings.MAXWORDS)
                continue;
            for(int i = 0; i < words.length; i++) {
                if(words[i].length() > 2) {
                    stringList.add(words[i]);
                }
            }
        }
        return stringList;
    }


    private void checkPenalty(List<String> list, String s)
    {
        List<String> sList = getStringList(new HashSet<String>(Arrays.asList(s.split(" "))));
        list.removeAll(sList);
    }


    public ArrayList<String[]> toRockitString() throws CodiException
    {
        ArrayList<String[]> result = new ArrayList<String[]>();
        for(int i = 0; i < list.size(); i++) {
            if(list.get(i).getConfidenceValue() != 0) {
                // result.add(list.get(i).toRockitString());
                Cell<? extends OWLEntity> cell = list.get(i);
                String[] rs = new String[] { cell.getSourceEntity().toString(), "<" + cell.getTargetEntity().getIRI().toString() + ">", Double.toString(cell.getConfidenceValue()) };
                if(cell.getTargetEntity().getIRI().getFragment() == null) {
                    rs[1] = "<" + Storage.target.ont.getOntologyID().getOntologyIRI().toString() + "#" + cell.getTargetEntity().getIRI().toURI().toString() + ">";
                }
                result.add(rs);
            }
        }
        // System.out.println(result.size());
        // for(int i = 0; i < result.size(); i++) {
        // System.out.println(result.get(i)[0] + " = " + result.get(i)[1]);
        // }

        return result;
    }


    public Mapping toMapping() throws CodiException
    {
        if(list.size() > 1) {
            Mapping mapping = new Mapping();
            for(Cell<?> cell : list) {
                if(cell.getConfidenceValue() != 0) {
                    mapping.push(cell.toCorrespondence());
                }
            }
            return mapping;
        }
        return new Mapping();
    }


    public List<Cell<? extends OWLEntity>> getList()
    {
        return list;
    }
}
